package de.pierrepoenisch.checkyoursite.server;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import de.pierrepoenisch.checkyoursite.client.CheckResult;
import de.pierrepoenisch.checkyoursite.client.CheckResultStateET;
import de.pierrepoenisch.checkyoursite.client.CheckSiteService;

/**
 * implementation of a service that checks a site and returns the result
 * @author pierre
 *
 */
public class CheckSiteServiceImpl extends RemoteServiceServlet implements CheckSiteService{
	String siteContent = new String();
	CheckResultStateET state;
	
	private static Logger logger = Logger.getRootLogger(); 
	
//	TODO move logging into an own file
//	TODO add exception logging
	static {
		//initialize logger
		PatternLayout layout = new PatternLayout("%d - %r [%t] %-5p %c %x - %m\n");
		ConsoleAppender consoleAppender = new ConsoleAppender(layout);
		logger.addAppender(consoleAppender);
		try {
			FileAppender fileAppender = new FileAppender(layout, 
					"CheckYourSite_Operating.log", false);
			logger.addAppender(fileAppender);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//		 ALL | DEBUG | INFO | WARN | ERROR | FATAL | OFF:
		logger.setLevel(Level.DEBUG);
		logger.info("Logger initialized.");
	}

	
	/**
	 * method that checkes a site with all it's linked sites
	 */
	public CheckResult checkSite(String uri){
		long tmpResult[];
		URL url;
		
		//log the requested URI
		logger.info("Checking Site: \"" + uri + "\"");
		
		state=CheckResultStateET.OK;
		
		//create correct URL
		if (! uri.toLowerCase().startsWith("http://")){
			uri="http://"+uri;
		}
		
		//		initialize result
		CheckResult result = new CheckResult();
		result.setURI(uri);
		
		// create url from uri string
		try {
			url = new URL(uri);
			//process the url
			tmpResult = processURL(url);
			result.setSiteSizeKB(tmpResult[0]/1024);
			result.setTimeInSec(tmpResult[1]);
			result.setState(state);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		String otherFiles[] = siteContent.toLowerCase().split("src");
		if ((otherFiles.length>1) && !(uri.endsWith(".js"))){
			//leave the first part of the string untouched
			for (int i=1;i<otherFiles.length;i++){
				otherFiles[i]=otherFiles[i].split(" ")[0];
				otherFiles[i]=otherFiles[i].split(">")[0];
				otherFiles[i]=otherFiles[i].split("}")[0];
				if (otherFiles[i].indexOf("\"")>0){
					otherFiles[i]=otherFiles[i].split("\"")[1];
				}else{
					otherFiles[i]=otherFiles[i].split("\'")[1];
				}
			}
			//special command to get rid of the first entry
			otherFiles[0]=otherFiles[1];
			otherFiles = deleteDoubleEntries(otherFiles);
			
			for (int i=0;i<otherFiles.length;i++){
				if (! otherFiles[i].toLowerCase().startsWith("http://")){
					otherFiles[i] = uri+otherFiles[i];
				}
				//reset the site content variable
				siteContent=new String();
				//check the site
				CheckResult tmpCheckResult = checkSite(otherFiles[i]);
				//add the result to the referred files
				CheckResult tmpRefFiles[] = result.getReferredFilesCheckResult();
				if (tmpRefFiles==null){
					tmpRefFiles = new CheckResult[1];
					tmpRefFiles[0]= tmpCheckResult;
					result.setReferredFilesCheckResult(tmpRefFiles);
				} else {
					CheckResult tmpNewRefFiles[] = new CheckResult[tmpRefFiles.length+1];
					for (int j=0; j<tmpRefFiles.length; j++){
						tmpNewRefFiles[j]=tmpRefFiles[j];
					}
					tmpNewRefFiles[tmpRefFiles.length]=tmpCheckResult;
					result.setReferredFilesCheckResult(tmpNewRefFiles);
				}
			}
		}		
		//return the result
		return result;
	}

	/**
	 * method to delete double entries in an array of strings (case sensitive)
	 * @param otherFiles
	 * @return an array of string with unique elements
	 */
	private String[] deleteDoubleEntries(String[] otherFiles) {
		String[] newOtherFiles = new String[otherFiles.length];
		//search for double entries and mark them with null
		for(int i=0;i<otherFiles.length;i++){
			boolean unique = true;
			for(int j=i+1; j<otherFiles.length;j++){
				if(otherFiles[i].equals(otherFiles[j])){
					unique=false;
				}
			}
			if (unique) {
				newOtherFiles[i]=otherFiles[i];
			}else{
				newOtherFiles[i]=null;
			}
		}
		
		//count unique elements
		int realLength=0;
		for (int i=0; i<newOtherFiles.length;i++){
			if (newOtherFiles[i] != null){
				realLength++;
			}
		}
		//create the new array with unique elements
		String result[] = new String[realLength];
		int indexResult = 0;
		for (int i=0; i<newOtherFiles.length;i++){
			if (newOtherFiles[i] != null){
				result[indexResult]=newOtherFiles[i];
				indexResult++;
			}
		}
		return result;
	}

	/**
	 * try to load the url and measure length and time needed
	 * @param url the url to process
	 * @param siteContent contains the site content when finished
	 * @return a long array with the length at the first position and the time at the second
	 */
	private long[] processURL(URL url){
		InputStream is;
		long length=0;
		long time=0;
		
		//logger.debug("processing URL: \"" + url + "\"");
		try {
			//create open the stream
			is = url.openStream();
			//initialize variables; we will read 10 bytes a time from the stream
			length = 0;			
			byte[] tmpArray = new byte[10];
			boolean bytesLeft = true;
			int count=-1;
			//while there are bytes left read them and process them
			while (bytesLeft){
				//read the next bytes
				count = is.read(tmpArray);
				//did we get any bytes
				if (count != -1){
					//count the total length
					length+=count;
					//add the new bytes to the content
					for (int i=0; i<count;i++){
						siteContent += (char) tmpArray[i];
					}
				}else{
					bytesLeft = false;
				}
			}
			//System.out.print(siteContent);
		} catch (MalformedURLException e) {
			state = CheckResultStateET.NOK;
			logger.info("Exception when checking "+url,e);
		} catch (FileNotFoundException e){
			state = CheckResultStateET.NOK;
			logger.info("Exception when checking "+url,e);
		} catch (IOException e) {
			state= CheckResultStateET.NOK;
			logger.info("Exception when checking "+url,e);
		}		
		long result[] = {length,time};
		return result;
	}
}
